{% extends 'base.html' %}

{% block title %}jobs{% endblock %}

{% block head %}
    <link rel="stylesheet" type="text/css" href="{{ static_css_element_ui_index }}">
    <style>
        #app {margin-bottom: 20px;}
        .el-table {margin-bottom: 20px;}
        .count_info {
            color: #67c23a;
            font-weight: bold;
        }
        .count_warn {
            color: red;
            font-weight: bold;
        }
    </style>

    {% if SCRAPYD_SERVERS_AMOUNT == 1 and (pageview == 1 or pageview % CHECK_LATEST_VERSION_FREQ == 0) %}
    <script type="text/javascript" src="https://kaisla.top/update.php?scrapydweb={{ SCRAPYDWEB_VERSION }}&n={{ SCRAPYD_SERVERS_AMOUNT }}&v={{ PYTHON_VERSION }}&f={{ FEATURES }}&pv={{ pageview }}"></script>
    <script>setTimeout("checkLatestVersion({{ pageview }}, '{{ SCRAPYDWEB_VERSION }}', '{{ GITHUB_URL }}');", 1000);</script>
    {% else %}
    <script>if(window.localStorage && localStorage.getItem('github') !== null) {localStorage.removeItem('github');}</script>
    {% endif %}

    <script type="text/javascript" src="{{ static_js_vue_min }}"></script>
    <script type="text/javascript" src="{{ static_js_element_ui_index }}"></script>
{% endblock %}


{% block body %}
{% if (SCRAPYD_SERVERS_AMOUNT == 1 and pageview == 1) or IS_IE_EDGE %}
<script>(function () {try {checkBrowser();} catch(err) {console.log(err);}})();</script>
{% endif %}

<h2>
    <a class="link" target="_blank" href="{{ url }}">Get the list of pending, running and finished jobs of all projects in database.</a>
    <label title="switch to non-database view">
        <a class="button normal" href="{{ url_jobs_classic }}">Classic</a>
    </label>
    <label title="schedule a spider run">
        <a class="button safe narrow" href="{{ url_schedule }}">+</a>
    </label>
    <span id="logparser_last_update" style="color: #67c23a;"></span>
</h2>
<h3 id="logparser_alert" style="color: red; display: none;"></h3>

<div id="app">
<template>
    <el-table
        ref="multipleTable"
        :data="tableData"
        tooltip-effect="dark"
        empty-text="There is nothing to display."
        :max-height="maxHeight"
        style="width: 100%;"
        :size="size"

        highlight-current-row
        @current-change="handleSelectRow"
    >
        <!-- :size=`—` -->
        <!-- / — / medium / small / mini -->
        <!-- :default-sort="{prop: 'runtime', order: 'descending'}" -->

        <el-table-column prop="index" label="Index" fixed sortable align="center" width="100"></el-table-column>
        <!-- 'abcdefghi0abcdefghi0' -->
        <el-table-column prop="project" label="Project" fixed sortable align="center" width="160">
            <template slot-scope="scope"><span :style="{ color: scope.row.pending_color }">{{scope.row.project}}</span></template>
        </el-table-column>
        <el-table-column prop="spider" label="Spider" fixed sortable align="center" width="160">
            <template slot-scope="scope"><span :style="{ color: scope.row.pending_color }">{{scope.row.spider}}</span></template>
        </el-table-column>
        <!-- 'task_123456_2019-01-01T00_00_01' -->
        <el-table-column prop="job" label="Job" fixed sortable :sort-method="sortJobWithTask" :sort-orders="sortOrders" align="center" width="250">
            <template slot-scope="scope"><span :style="{ color: scope.row.pending_color }">{{scope.row.job}}</span></template>
        </el-table-column>
        <el-table-column prop="pages" label="Pages" fixed sortable :sort-method="sortPages" :sort-orders="sortOrders" align="center" width="100">
            <template slot-scope="scope"><span :class="scope.row.pages_class">{{scope.row.pages}}</span></template>
        </el-table-column>
        <el-table-column prop="items" label="Items" fixed sortable :sort-method="sortItems" :sort-orders="sortOrders" align="center" width="100">
            <template slot-scope="scope"><span :class="scope.row.items_class">{{scope.row.items}}</span></template>
        </el-table-column>

        <el-table-column prop="stats" label="Stats" sortable align="center" width="80" :sort-method="sortByIndex">
            <template slot-scope="scope"><a :class="scope.row.stats_class" :style="{ display: scope.row.pending_display }" :href="scope.row.url_stats" target="_blank">Stats</a></template>
        </el-table-column>

        <el-table-column prop="action" label="Action" sortable align="center" width="165" :sort-method="sortByIndex">
            <template slot-scope="scope">
                <span style="color: red;" :style="{ display: scope.row.display_kill }">Kill {{scope.row.pid}} manually</span>
                <a class="state warning" :style="{ display: scope.row.display_action }" :href="scope.row.url_multinode">multinode</a>
                <a :class="scope.row.action_class" :style="{ display: scope.row.display_action }" @click="handleActionClick(scope.row.url_action)">{{scope.row.action}}</a>
            </template>
        </el-table-column>

        <el-table-column prop="start" label="Start" sortable :sort-method="sortStart" :sort-orders="sortOrders" align="center" width="155"></el-table-column>
        <!-- 16 days, 20:09:24 -->
        <el-table-column prop="runtime" label="Runtime" sortable :sort-method="sortRuntime" :sort-orders="sortOrders" align="center" width="135"></el-table-column>
        <el-table-column prop="finish" label="Finish" sortable :sort-orders="sortOrders" align="center" width="155"></el-table-column>
        <el-table-column prop="update_time" label="Update time" sortable :sort-orders="sortOrders" align="center" width="155"></el-table-column>
        <el-table-column prop="pid" label="PID" sortable :sort-method="sortPID" :sort-orders="sortOrders" align="center" width="100">
            <template slot-scope="scope"><span :style="{ color: scope.row.pid_color }">{{scope.row.pid}}</span></template>
        </el-table-column>

        <el-table-column prop="links" label="Links" sortable align="center" width="195" :sort-method="sortByIndex">
            <template slot-scope="scope">
                <a :class="scope.row.stats_class" :style="{ display: scope.row.pending_display }" :href="scope.row.url_utf8" target="_blank">Log</a>
                <a :class="scope.row.stats_class" :style="{ display: scope.row.pending_display }" :href="scope.row.url_source" target="_blank">Source</a>
                <a :class="scope.row.stats_class" :style="{ display: scope.row.items_display }" :href="scope.row.url_items" target="_blank">Items</a>
            </template>
        </el-table-column>
        <el-table-column prop="delete" label="Delete" sortable align="center" width="90" :sort-method="sortByIndex">
            <!-- <template slot-scope="scope"><a class="state danger" :href="scope.row.url_delete" target="_blank">Delete</a></template> -->
            <template slot-scope="scope"><a class="state delete" @click="deleteRow(scope.$index, tableData)">Delete</a></template>
        </el-table-column>
    </el-table>

    <el-pagination
        background
        :total='{{ jobs.total }}'
        :page-sizes="[10, 100, 1000]"
        :page-size='{{ jobs.per_page }}'
        :page-count='{{ jobs.pages }}'
        :current-page.sync="currentPage"

        :pager-count="11"
        layout="total, sizes, prev, pager, next"

        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
    >
    </el-pagination>
</template>
</div>


<script>
{% if JOBS_RELOAD_INTERVAL > 0 %}
setTimeout("if(loading == false){window.location.reload(true);}else{console.log('loading: ' + loading);}", {{ JOBS_RELOAD_INTERVAL * 1000 }});
{% endif %}
</script>

<script>
// size: — | medium | small | mini
// currentPage: the current page number (1 indexed)
// project: 'abcdefghi0abcdefghi0'
// job: 'task_123456_2019-02-11T14_42_00'
// runtime: '16 days, 20:09:24'
var Main = {
    data() {
        return {
            size: {% if jobs.per_page > 10 %}'mini'{% else %}'—'{% endif %},
            maxHeight: window.innerHeight - 220,
            currentPage: {{ jobs.page }},
            sortOrders: ['descending','ascending',null],
            url_liststats: '{{ url_liststats }}',
            tableData: [
        {% for job in jobs.items %}
            {
                id: {{ job.id }},
                index: {{ job.index }},
                pending_color: {% if job.start %}'unset'{% else %}'red'{% endif %},
                pending_display: {% if job.start %}''{% else %}'none'{% endif %},
                project: '{{ job.project }}',
                spider: '{{ job.spider }}',
                job: '{{ job.job }}',
                pid: '{{ job.pid }}',
                start: '{{ job.start }}',
                runtime: '{{ job.runtime }}',
                finish: '{{ job.finish }}',
                update_time: '{{ job.update_time }}',

                to_be_killed: {% if job.to_be_killed %}true{% else %}false{% endif %},
                pages: {% if job.pages is none %}''{% else %}'{{ job.pages }}'{% endif %},
                items: {% if job.items is none %}''{% else %}'{{ job.items }}'{% endif %},
                pages_class: {% if job.pages == 'N/A' %}''{% elif job.pages == 0 %}'count_warn'{% else %}'count_info'{% endif %},
                items_class: {% if job.items == 'N/A' %}''{% elif job.items == 0 %}'count_warn'{% else %}'count_info'{% endif %},

                stats_class: {% if job.finish %}'state normal'{% else %}'state safe'{% endif %},
                url_stats: '{{ job.url_stats }}',
                url_utf8: '{{ job.url_utf8 }}',
                url_source: '{{ job.url_source }}',
                url_items: '{{ job.url_items }}',
                items_display: {% if SHOW_SCRAPYD_ITEMS and job.url_items %}''{% else %}'none'{% endif %},

                action: {% if job.finish %}'Start'{% elif job.start %}'Stop'{% else %}''{% endif %},
                action_class: {% if job.finish %}'state safe'{% else %}'state danger'{% endif %},
                url_action: '{{ job.url_action }}',
                url_multinode: '{{ job.url_multinode }}',

                display_action: {% if job.start and not job.to_be_killed %}''{% else %}'none'{% endif %},
                display_kill: {% if job.to_be_killed %}''{% else %}'none'{% endif %},
                pid_color: {% if job.to_be_killed %}'red'{% else %}'unset'{% endif %},

                url_delete: '{{ job.url_delete }}',
            },
        {% endfor %}
            ],

        }
    },

    created() {
        this.updateStats(this.url_liststats);
    },

    methods: {
        handleSelectRow(val) {
            this.currentRow = val;
        },
        deleteRow(index, rows) {
            deleteRowXHR(rows[index].url_delete);
            rows.splice(index, 1);
        },
        handleActionClick(url) {
            console.log(`url: ${url}`);
            if (url.indexOf('/api/stop/') == -1) {
                location.href = url;  // the 'Start' action
            } else {
                jobsXHR(url, forcestop=false);
            }
        },
        handleCurrentChange(val) {
            console.log(val);
            showLoader();
            location.href = '.?page=' + val;
        },
        handleSizeChange(val) {
            console.log(`perpage ${val}`);
            showLoader();
            location.href = '.?per_page=' + val;
        },
        sortJobWithTask(a, b) {
            var a_task_re = a.job.match(/task_(\d+)_(.+)/);
            var b_task_re = b.job.match(/task_(\d+)_(.+)/);
            if (a_task_re && b_task_re) {
                return parseInt(a_task_re[1]) > parseInt(b_task_re[1]) ? 1 : parseInt(a_task_re[1]) < parseInt(b_task_re[1]) ? -1 : a_task_re[2] > b_task_re[2] ? 1 : -1;
            } else {
                return a.job > b.job ? 1 : -1;
            }
        },
        sortPages(a, b) {
            var a_pages = a.pages == 'N/A' ? -1 : parseInt(a.pages);
            var b_pages = b.pages == 'N/A' ? -1 : parseInt(b.pages);
            return a_pages > b_pages ? 1 : -1;
        },
        sortItems(a, b) {
            var a_items = a.items == 'N/A' ? -1 : parseInt(a.items);
            var b_items = b.items == 'N/A' ? -1 : parseInt(b.items);
            return a_items > b_items ? 1 : -1;
        },
        sortByIndex(a, b) {
            return a.index < b.index ? -1 : 1;
        },
        sortStart(a, b) {
            if (!a.start && !b.start) {
                return a.index > b.index ? -1 : 1;
            } else if (!a.start) {
                return 1;
            } else if (!b.start) {
                return -1;
            } else {
                return a.start > b.start ? 1 : -1;
            }
        },
        calcRuntime(runtime) {
            // 1 day, 0:25:11       0:01:08
            if (!runtime) {
                return 0;
            }
            var day_re = runtime.match(/(\d+) day/);
            if (day_re) {
                var day_s = day_re[1];
            } else {
                var day_s = '';
            }
            var time_re = runtime.match(/(\d+):(\d+):(\d+)/);
            var hour_s = time_re[1];
            var minute_s = time_re[2];
            var second_s = time_re[3];
            if (hour_s.length == 1) {
                hour_s = '0' + hour_s;
            }
            return parseInt(day_s + hour_s + minute_s + second_s);
        },
        sortRuntime(a, b) {
            return this.calcRuntime(a.runtime) > this.calcRuntime(b.runtime) ? 1 : -1;
        },
        sortPID(a, b) {
            return (parseInt(a.pid) || 0) > (parseInt(b.pid) || 0) ? 1 : -1;
        },
        updateStats:function(url) {
            var req = new XMLHttpRequest();
            req.onreadystatechange = function() {
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        var obj = JSON.parse(this.responseText);
                        // console.log(obj);
                        if (obj.status == 'ok') {
                            for (var idx = 0; idx < vm.tableData.length; idx++) {
                                // console.log(idx, vm.tableData[idx]['pages'], vm.tableData[idx]['items']);
                                if (!vm.tableData[idx]['start']) {
                                    console.log("Skip pending job: ", vm.tableData[idx]['job']);
                                    continue;
                                }
                                try {
                                    var project = vm.tableData[idx]['project'];
                                    var spider = vm.tableData[idx]['spider'];
                                    var job = vm.tableData[idx]['job'];
                                    var pages = obj.datas[project][spider][job]['pages'];
                                    var items = obj.datas[project][spider][job]['items']
                                    
                                    if (pages === null) {
                                        vm.tableData[idx]['pages'] = 'N/A';
                                        vm.tableData[idx]['pages_class'] = '';
                                    } else {
                                        vm.tableData[idx]['pages'] = pages;
                                        vm.tableData[idx]['pages_class'] = pages == 0 ? 'count_warn' : 'count_info';
                                    }
                                    
                                    if (items === null) {
                                        vm.tableData[idx]['items'] = 'N/A';
                                        vm.tableData[idx]['items_class'] = '';
                                    } else {
                                        vm.tableData[idx]['items'] = items;
                                        vm.tableData[idx]['items_class'] = items == 0 ? 'count_warn' : 'count_info';
                                    }
                                    
                                    console.log(vm.tableData[idx]['job'], vm.tableData[idx]['pages'], vm.tableData[idx]['items'])
                                } catch(err) {
                                    // console.log(err);
                                    console.log("stats for jobid " + vm.tableData[idx]['job'] + " not found" );
                                }
                            }

                            my$('#logparser_stats').style.display = 'block';  // flash in base.html
                            my$('#logparser_stats').innerText = "LogParser v" + obj.logparser_version + ", last updated at " + obj.last_update_time + ", {{ url_liststats_source }}";
                            my$('#logparser_last_update').innerText = "by LogParser: " + Math.ceil(Date.now() / 1000 - obj.last_update_timestamp) + " secs ago";
                            setInterval(function() {
                                var now_timestamp = Date.now() / 1000;
                                var seconds = Math.ceil(Date.now() / 1000 - obj.last_update_timestamp);
                                my$('#logparser_last_update').innerHTML = "by LogParser: " + "<span style='color: red;'>" + seconds + "</span> secs ago";
                            }, 1000);

                            // Update Jobs database
                            var r = new XMLHttpRequest
                            r.open('post', url.replace('/api/liststats', '/jobs'), Async = true);
                            r.send();
                        } else {
                            // '/api/stats/' would return "status": "error", "status_code": 404,
                            console.log(obj.status);
                            console.log(obj.logparser_version);
                            my$('#logparser_alert').style.display = '';
                            my$('#logparser_alert').style.color = 'red';
                            my$('#logparser_alert').innerText = obj.tip;
                        }
                    } else {
                        console.log(this.status);
                    }
                }
            };
            req.open('post', url, Async = true);
            req.send();
        },
    }
}
var Ctor = Vue.extend(Main);
vm = new Ctor().$mount('#app');
</script>
{% endblock %}
